package lru

import "container/list"

// Cache is lru cache, delete long time no use data
type Cache struct {
	maxBytes int64                    // max memory can use
	nBytes   int64                    // have use how much memory
	ll       *list.List               // use double linked list save Value
	cache    map[string]*list.Element // use map save key linked Value to double linked list
	OnDelete func(string, Value)      // delete one row data to do something
}

// double linked list item
type entry struct {
	key   string
	value Value
}

// value need Len function, calculate speed bytes
type Value interface {
	Len() int
}

func New(maxBytes int64, deleteFunc func(string, Value)) *Cache {
	return &Cache{
		maxBytes: maxBytes,
		ll:       list.New(),
		cache:    make(map[string]*list.Element),
		OnDelete: deleteFunc,
	}
}

func (c *Cache) Get(key string) (value Value, ok bool) {
	if ele, ok := c.cache[key]; ok {
		// get element from map, move this element to front
		c.ll.MoveToFront(ele)
		return ele.Value.(entry).value, true
	}

	return
}

func (c *Cache) RemoveOldest() {
	ele := c.ll.Back()
	if ele != nil {
		kv := ele.Value.(entry)
		delete(c.cache, kv.key)
		c.ll.Remove(ele)
		c.nBytes -= int64(len(kv.key)) + int64(kv.value.Len())
		if c.OnDelete != nil {
			c.OnDelete(kv.key, kv.value)
		}
	}
}

// add one row data or update one row data
func (c *Cache) Add(key string, value Value) {
	if ele, ok := c.cache[key]; ok {
		// update
		c.ll.MoveToFront(ele)
		v := ele.Value.(entry)
		v.value = value
		c.nBytes += int64(value.Len()) - int64(v.value.Len())
	} else {
		// add
		ele = c.ll.PushFront(entry{key: key, value: value})
		c.cache[key] = ele
		c.nBytes += int64(len(key)) + int64(value.Len())
	}
}

// return cache data count
func (c *Cache) Len() int {
	return c.ll.Len()
}
